home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / ccmd / datime.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-19  |  38.6 KB  |  1,266 lines

  1. /*
  2.  Author: Andrew Lowry
  3.  
  4.  Columbia University Center for Computing Activities, July 1986.
  5.  Copyright (C) 1986, 1987, Trustees of Columbia University in the City
  6.  of New York.  Permission is granted to any individual or institution
  7.  to use, copy, or redistribute this software so long as it is not sold
  8.  for profit, provided this copyright notice is retained.
  9. */
  10. /* TODO: Finish dtdst, figure out why always GMT */
  11.  
  12. /* datime
  13. **
  14. ** Utilities to deal with times and dates.  Two routines are machine-
  15. ** specific and must be written for each machine individually.  These
  16. ** are dtnow, which fills in a datime structure with the current
  17. ** date and time, and dttzone, which returns the local timezone in
  18. ** minutes west of Greenwich and a timezone id, if available.
  19. **
  20. ** Required structure and constant declarations are to be found in datime.h.
  21. ** tzone.h contains declarations of timezone information, separated from
  22. ** this source for ease in modifying the rules.
  23. **/
  24.  
  25. #include "ccmdlib.h"
  26. #include "tzone.h"            /* declare timezone info structures */
  27. #include "dtpat.h"            /* get date patterns */
  28.  
  29. /* A few useful character constants */
  30.  
  31. #define NULCHAR    '\000'
  32. #define TAB    '\011'
  33. #define SPACE    '\040'
  34.  
  35.  
  36.  
  37. /* dtnow
  38. **
  39. ** Purpose:
  40. **   Return, via a datime structure supplied by the caller, the current
  41. **   date and time, using local timezone information.
  42. **
  43. ** Input arguments:
  44. **   dtblk - Pointer to a datime block to be filled in.
  45. **
  46. ** Output arguments:
  47. **   dtblk - Fields filled in with current info.
  48. **
  49. ** Returns: Nothing.
  50. **/
  51.  
  52. #ifdef MSDOS
  53. /* #include <dos.h> */
  54.  
  55. /* macro to convert from 2-digit BCD to integer */
  56.  
  57. #define bcd2int(x) (((x)&0xf)+(10*(((x)>>4)&0xf)))
  58.  
  59. dtnow(dtblk)
  60. datime *dtblk;
  61. {
  62.   union REGS inregs, outregs;    /* structures for communicating with BIOS */
  63.   int junk;
  64.  
  65.   inregs.h.ah = 2;        /* read the time */
  66.   int86(0x1a,&inregs,&outregs);
  67.   dtblk->_dthr = bcd2int(outregs.h.ch); /* transfer values */
  68.   dtblk->_dtmin = bcd2int(outregs.h.cl);
  69.   dtblk->_dtsec = bcd2int(outregs.h.dh);
  70.  
  71.   inregs.h.ah = 4;        /* read the date */
  72.   int86(0x1a,&inregs,&outregs);
  73.   dtblk->_dtmon = bcd2int(outregs.h.dh)-1; /* transfer values */
  74.   dtblk->_dtday = bcd2int(outregs.h.dl)-1;
  75.   dtblk->_dtyr = bcd2int(outregs.h.cl)+100*bcd2int(outregs.h.ch);
  76.                 /* compute day-of-week */
  77.   dtblk->_dtdow = dtdow(dtblk->_dtmon,dtblk->_dtday,dtblk->_dtyr);
  78.   dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* get local timezone info */
  79.   dtblk->_dtdst = dtdst(dtblk); /* compute daylight savings time */
  80. }
  81. #endif
  82.  
  83. #if unix
  84.  
  85. dtnow(dtblk)
  86. datime *dtblk;
  87. {
  88.   long t;
  89.   struct tm *localtime(), *x;
  90.   t = time(NULL);            /* get time */
  91.   x = localtime(&t);            /* convert it */
  92.   dtblk->_dthr = x->tm_hour;        /* hour */
  93.   dtblk->_dtmin = x->tm_min;        /* minute */
  94.   dtblk->_dtsec = x->tm_sec;        /* second */
  95.   dtblk->_dtmon = x->tm_mon;        /* month */
  96.   dtblk->_dtday = x->tm_mday -1;    /* day of month */
  97.   dtblk->_dtyr = x->tm_year+1900;    /* year */
  98.   dtblk->_dtdow = x->tm_wday;        /* week day */
  99.   dttzone(&dtblk->_dttz,&dtblk->_dttzc);/* time zone */
  100.   dtblk->_dtdst = dtdst(dtblk);        /* daylight savings time */
  101. }
  102. #endif /* unix */
  103.  
  104.  
  105. /* dttzone
  106. **
  107. ** Purpose:
  108. **   Returns the local timezone, as minutes west of Greenwich.  Daylight
  109. **   savings time is not considered.  If a timezone ID is known, it is
  110. **   returned as well.
  111. **
  112. ** Input arguments: None.
  113. ** Output arguments:
  114. **   offset - Number of minutes west of Greenwich, in range -7199 to 7200
  115. **     (that is, the international dateline is considered 12 hours WEST,
  116. **     not 12 hours EAST of Greenwich)
  117. **   code - Timezone code number if known, or -1
  118. **
  119. ** Returns: Nothing
  120. **/
  121.  
  122. #if MSDOS
  123. dttzone(offset,code)
  124. int *offset,*code;
  125. {
  126.   *offset = LCL_TZOFF;        /* get offset defined in datime.h */
  127. #ifdef LCL_TZCODE
  128.   *code = LCL_TZCODE;        /* and code from datime.h if there is one */
  129. #else
  130.   *code = -1;            /* otherwise signal no code */
  131. #endif
  132. }
  133. #endif
  134.  
  135. #if (BSD)
  136. dttzone(offset, code)
  137. int *offset, *code;
  138. {
  139.   struct timeval tp;
  140.   struct timezone tz;
  141.   gettimeofday(&tp,&tz);        /* get time of day */
  142.   *offset = tz.tz_minuteswest;
  143. #ifdef LCL_TZCODE
  144.   *code = LCL_TZCODE;           /* and code from datime.h if there is one */
  145. #else
  146.   *code = -1;                /* otherwise signal no code */
  147. #endif
  148. }  
  149. #endif /*  BSD */
  150.  
  151. #ifdef SYSV
  152. dttzone(offset, code)
  153. int *offset, *code;
  154. {
  155.   *offset = timezone / 60;        /* get the correct timezone */
  156. #ifdef LCL_TZCODE
  157.   *code = LCL_TZCODE;           /* and code from datime.h if there is one */
  158. #else
  159.   *code = -1;                /* otherwise signal no code */
  160. #endif
  161. }  
  162. #endif /*  SYSV */
  163.  
  164.  
  165.  
  166.  
  167. /* dttzinf
  168. **
  169. ** Purpose:
  170. **   Return a pointer to a timezone information structure, given
  171. **   a timezone code or an offset from Greenwich.  The former style
  172. **   of call is identified by the input argument being a positive
  173. **   value with flag TZ_CODE set.  Anything else is taken to be
  174. **   a signed number of minutes west of Greenwich.  In the former
  175. **   case, the tzinfo array is scanned for a timezone with the given
  176. **   code, and a pointer to the corresponding tzinf structure is
  177. **   returned.  When an offset is given, the first timezone with
  178. **   the given offset is returned (so timezones with different
  179. **   dst rules, or in different hemispheres, cannot be distinguished
  180. **   by means of their offsets).  In either case, if no matching
  181. **   timezone is located, NULL is returned.
  182. **
  183. ** Input arguments:
  184. **   key - Signed offset in minutes west of Greenwich, or positive
  185. **     timezone code value with the TZ_CODE flag turned on.
  186. **
  187. ** Output arguments: None.
  188. ** Returns: Pointer to first matching tzinf structure, or NULL if none found.
  189. **/
  190.  
  191. tzinf *
  192. dttzinf(key)
  193. int key;
  194. {
  195.   int bycode;                /* TRUE if looking up by code */
  196.   int i;
  197.  
  198.   if ((key > 0) && (key & TZ_CODE)) {    /* lookup by code? */
  199.     bycode = TRUE;            /* set flag */
  200.     key &= ~TZ_CODE;            /* and isolate the code value */
  201.   }
  202.   else
  203.     bycode = FALSE;            /* otherwise clear flag */
  204.  
  205.   for (i = 0; i < TZCOUNT; i++)        /* loop through the table */
  206.     if (key == (bycode ? tzinfo[i]._tzcode : tzinfo[i]._tzoff)) 
  207.       return(&tzinfo[i]);        /* found indicated zone */
  208.   return(NULL);                /* zone not in table */
  209. }
  210.  
  211.  
  212.  
  213. /* dtdow
  214. **
  215. ** Purpose:
  216. **   Given a date, compute the day of week on which the date falls,
  217. **   according to the Gregorian calendar system.  The calculation
  218. **   assumes that the Gregorian system has been in effect since
  219. **   1-Jan-0000, which is not true.  In general, correct results
  220. **   will be given for dates after 1923, by which time all the
  221. **   major cultures had adopted the calendar.  See a world almanac
  222. **   or other source for details of who joined the system when.
  223. **
  224. ** Input arguments:
  225. **   mon - The month for the date in question, 0 = January
  226. **   day - The day of the month, with 0 = the first day.
  227. **   yr - The year in question, with no conversion for years
  228. **     less than 100.
  229. ** 
  230. ** Output arguments: None.
  231. ** Returns: Day of week for given date, with 0 = Sunday, 6 = Saturday.
  232. **/
  233.  
  234. /* Useful macro to decide if a year is a leap year */
  235.  
  236. #define LEAP(y)    (((y)%400 == 0) || (((y)%100 != 0) && ((y)%4 == 0)))
  237.  
  238. /* lengths of the months, assuming a leap year */
  239. static int mthlens[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  240.  
  241. int
  242. dtdow(mon,day,yr)
  243. int mon,day,yr;
  244. {
  245.   int dow;                /* calculated day of week */
  246.   int i;
  247.   
  248.   /* First calculate day-of-week relative to 1-Jan-0000, that is,
  249.   ** the number of days since that date, modulo 7.  A first approximation
  250.   ** accounts for years prior to the given year, by calculating (365*yr) % 7.
  251.   ** But since 365%7 is 1, this is the same as (1*yr) % 7, or yr % 7.
  252.   ** Adjustments are then made for leap years, and then for preceding months
  253.   ** in the given year, and preceding days in the given month.
  254.   **/
  255.   dow = yr % 7;                /* start with days in prior years */
  256.   dow += (yr+3) / 4;            /* adjust for preceding leap years */
  257.   dow -= (yr+99) / 100;            /* most centuries are non-leap */
  258.   dow += (yr+399) / 400;        /* but every fourth one is */
  259.   for (i = 0; i < mon; i++)
  260.     dow += mthlens[i];            /* add preceding months this year */
  261.   if ((mon > 1) && !LEAP(yr))        /* after non-leap Feb? */
  262.     dow -= 1;                /* yup, we added to much */
  263.   dow += day;                /* now adjust for date within month */
  264.   dow += 6;                /* Greg 1-Jan-0000 was a Saturday */
  265.   dow %= 7;                /* get dow in range 0-7 */
  266.   return(dow);                /* give result to caller */
  267. }
  268.  
  269.  
  270.  
  271. /* dtdst
  272. **
  273. ** Purpose:
  274. **   Given a datime structure filled in with date, time, and timezone
  275. **   information, compute the daylight-savings-time adjustment that
  276. **   should be in effect according to whatever rules we have for the
  277. **   given timezone.  If no appropriate rule exists, assume no adjustment
  278. **   (that is, an adjustment of zero).  Generally, when dst is in effect,
  279. **   the adjustment will be -60, meaning that to convert to standard time,
  280. **   60 minutes should be subtracted from the given time.  If a timezone
  281. **   code is present in the datime structure, it is used to find the dst
  282. **   rules.  Otherwise, the timezone offset is used.  In the latter case,
  283. **   the wrong rules may be applied since timezones with identical offsets
  284. **   but different dst rules will not be distinguished.
  285. **
  286. ** Input arguments:
  287. **   dtblk - A pointer to the datime block holding the date, time, and zone
  288. **     information to use.
  289. **
  290. ** Output arguments: None.  (In particular, the _dtdst field of the given
  291. **   datime block is NOT modified.)
  292. **
  293. ** Returns: Number of minutes to add to given time in order to convert
  294. **   to standard time in its timezone (so adding this value as well
  295. **   as the _dttz value will result in Greenwich Mean Time).
  296. **
  297. **/
  298.  
  299. int
  300. dtdst(dtblk)
  301. datime *dtblk;
  302. {
  303.   tzinf *tzi;            /* time zone info block */
  304.   dstrule *rule;        /* applicable dst rule */
  305.   int julian, dston, dstoff, i;
  306.  
  307.   if (dtblk->_dttzc != -1)
  308.     tzi = dttzinf(TZ_CODE | dtblk->_dttzc); /* look up by code if set */
  309.   else
  310.     tzi = dttzinf(dtblk->_dttz); /* otherwise look up by offset */
  311.  
  312.   if (tzi == NULL) 
  313.     return(0);            /* no adjustment if tz not known */
  314.  
  315.   /* calculate julian date of current date */
  316.   for (julian = dtblk->_dtday+1, i = 0; i < dtblk->_dtmon; i++)
  317.     julian += mthlens[i];
  318.   if ((dtblk->_dtmon > 1) && !LEAP(dtblk->_dtyr))
  319.     julian--;
  320.   
  321.   for (rule = tzi->_tzdst; ; rule++) {
  322.     if (dtblk->_dtyr < rule->_dsyr1 || dtblk->_dtyr > rule->_dsyrn) {
  323.       if (rule->_dsyr1 > 0)
  324.     continue;
  325.       else return 0;
  326.     }
  327.     
  328.     /* get appropximate dates of dst on/off */
  329.     dston = rule->_dsdow;    /* XXX struct member is mislabeled */
  330.     dstoff = rule->_dsday;    /* XXX struct member is mislabeled */
  331.     
  332.     /* account for feb 29th */
  333.     if (!LEAP(dtblk->_dtyr)) {
  334.       if (dston >= 59)
  335.     dston--, dstoff--;
  336.       else
  337.     if (dstoff >= 59)
  338.       dstoff--;
  339.     }
  340.     
  341.     /*
  342.      * get actual julian dates for dst on/off, assumed to be Sunday
  343.      * previous to the approximate date.
  344.      */
  345.     
  346.     /* get days from Jan 1st to first Sunday */
  347.     i = (julian - 1 + 7 - dtblk->_dtdow) % 7;
  348.     /* subtract number of days between dston and previous Sunday */
  349.     dston -= ((((dston - 1) % 7) - i) + 7) % 7;
  350.     dstoff -= ((((dstoff - 1) % 7) - i) + 7) % 7;
  351.     
  352.  
  353.     if ((julian < dston) || (julian > dstoff) ||
  354.     (julian == dston && (dtblk->_dthr < 2)) ||
  355.     (julian == dstoff && (dtblk->_dthr >= (tzi->_tzsth ? 2 : 1)))) {
  356.       if (rule->_dsyr1)
  357.         continue;
  358.       else return (0);
  359.     }
  360.     return (- tzi->_tzdadj);
  361.   }
  362. }
  363.  
  364.  
  365.  
  366. /* dtparse
  367. **
  368. ** Purpose:
  369. **   Attempt to interpret a given input string as a date/time specification.
  370. **   If successful, fill the results into a given datime block.  A successful
  371. **   parse need not consume all the available input.
  372. **   
  373. **   The input must match one of the patterns declared in dtpat.h, which
  374. **   cover a wide range of formats.  Flags may be passed to constrain
  375. **   which patterns may be used, or to indicate that either the time or
  376. **   the date portion is not to be parsed.  The patterns are ranked so
  377. **   that a pattern of a higher rank will be chosen when there is
  378. **   ambiguity in the input.  The ranking can be supplemented by a set of
  379. **   adjustments that depend on the flags that are specified in the parse,
  380. **   so that the ranking can depend on the flags.  For details of how
  381. **   the patterns are constructed, read the comments in dtpat.h.
  382. **
  383. **   Parsing succeeds if, of all the patterns matching the current input,
  384. **   there is a single matching pattern of highest rank.  The input is
  385. **   decoded according to that pattern, checked for acceptability, and
  386. **   then returned via the  datime structure supplied by the caller. 
  387. **
  388. **   Completion text is set by some pattern elements when they encounter
  389. **   the end of the input.
  390. **
  391. ** Input arguments:
  392. **   flags - Flags to control the parse, as defined in datime.h
  393. **   text - A pointer to the text to be parsed.
  394. **   textlen - The number of characters in the text to be parsed.
  395. **
  396. ** Output arguments:
  397. **   dtblk - Filled in with date/time info from parse if successful.
  398. **     If time is not given, 00:00:01 is used, with the local timezone
  399. **     and daylight savings computed by the rules.  If date is not
  400. **     given, the current date is filled in.  Day of week is computed
  401. **     from the date, and if the input specifies a day of week, it
  402. **     must match the computed day in order to succeed.
  403. **   parselen - The number of characters consumed by a successful parse.
  404. **   completion - A pointer to text that can be used to complete
  405. **     the current input (generally only partial completion results)
  406. **     if the date/time specification is incomplete.
  407. **   incomplete - TRUE iff the parse failed due to lack of sufficient
  408. **     input.
  409. **
  410. ** Returns: TRUE iff the parse succeeds.
  411. **/
  412.  
  413. /* Define TRACE here if a pattern match trace is desired */
  414.  
  415. /* #define TRACE */
  416.  
  417. /* Global variables used by the pattern matching routines */
  418.  
  419. static char *dttext;        /* pointer to input text */
  420. static int dtlen;        /* # of chars left to parse */
  421. static int dtinc;        /* parse failed for lack of text */
  422. static char *dtcmp;        /* pointer to completion text after */
  423.                 /*  incomplete parse */
  424.  
  425. int
  426. dtparse(flags,text,textlen,dtblk,parselen,completion,incomplete)
  427. int flags,textlen,*parselen,*incomplete;
  428. char *text,**completion;
  429. datime *dtblk;
  430. {
  431.   datime curdt;            /* current time/date go here */
  432.   dtpat *pats;            /* pattern array selected by flags */
  433.  
  434.   dttext = text;        /* set up global variables */
  435.   dtlen = textlen;
  436.   dtinc = *incomplete = FALSE;    /* assume not incomplete */
  437.  
  438.   if (flags & DTP_NDA)        /* check exclusions from parse */
  439.     if (flags & DTP_NTI)
  440.       return(FALSE);        /* cannot exlude both time and date */
  441.     else
  442.       pats = timpats;        /* exclude date but not time */
  443.   else
  444.     if (flags & DTP_NTI)
  445.       pats = datpats;        /* exclude time but not date */
  446.     else
  447.       pats = dtpats;        /* exclude neither */
  448.   if (!match(pats,flags)) {    /* attempt a parse */
  449.     if (dtinc) {        /* reached end of input? */
  450.       *incomplete = TRUE;    /* yup, let them know */    
  451.       *completion = dtcmp;    /* and pass on completion text */
  452.     }
  453.     return(FALSE);        /* give a failure return */
  454.   }
  455.   if (flags & DTP_NDA) {    /* need current date info? */
  456.     dtnow(&curdt);        /* get it */
  457.     dtblk->_dtmon = curdt._dtmon; /* and transfer date info */
  458.     dtblk->_dtday = curdt._dtday;
  459.     dtblk->_dtyr = curdt._dtyr;
  460.     dtblk->_dtdow = curdt._dtdow;
  461.   }
  462.   else {
  463.     dtblk->_dtmon = mon;    /* otherwise transfer parsed values */
  464.     dtblk->_dtday = day;
  465.     dtblk->_dtyr = yr;
  466.     dtblk->_dtdow = dow;
  467.   }
  468.   
  469.   if (flags & DTP_NTI) {    /* time not parsed? */
  470.     dtblk->_dthr = 0;        /* fill in one second past midnight */
  471.     dtblk->_dtmin = 0;
  472.     dtblk->_dtsec = 1;
  473.     dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* get local timezone info */
  474.     dstspec = -1;        /* need dst computation */
  475.   }
  476.   else {            /* parsed time - fill in values */
  477.     dtblk->_dthr = hr;
  478.     dtblk->_dtmin = min;
  479.     dtblk->_dtsec = sec;
  480.     if (tzspec != -1) {        /* timezone given? */
  481.       dtblk->_dttz = tz;    /* then transfer it */
  482.       dtblk->_dttzc = tzc;
  483.     }
  484.     else {
  485.       dttzone(&dtblk->_dttz,&dtblk->_dttzc); /* otherwise get local zone */
  486.       dstspec = -1;        /* and no dst indicator given */
  487.     }
  488.   }
  489.   if (dstspec == -1)        /* dst indicator not given? */
  490.     dtblk->_dtdst = dtdst(dtblk); /* then compute it by the rules */
  491.   else
  492.     dtblk->_dtdst = dst;    /* else set value from parse */
  493.  
  494.   *parselen = dttext - text;    /* set # of chars consumed */
  495.   
  496.   return(TRUE);            /* and succeed */
  497. }
  498.  
  499.  
  500. /* The pattern matcher routines */
  501.  
  502. #define MAXVAL    2
  503. static int patval[MAXVAL];        /* values set by pattern elements */
  504.  
  505. /* match
  506. ** 
  507. ** Purpose:
  508. **   Apply a given set of patterns to the text currently pointed to by
  509. **   global variable 'dttext'.  If there is a single succeeding pattern
  510. **   of highest rank, consume the characters and return TRUE.  Otherwise
  511. **   return FALSE.  As soon as a highest-rank failing pattern sets the 
  512. **   dtinc flag, a FALSE return is made immediately without attempting 
  513. **   other patterns.  On success, the index of the succeeding pattern in 
  514. **   the pattern array is returned via the patval array.
  515. **
  516. ** Input arguments:
  517. **   pats - Pointer to the first pattern in an array of patterns to attempt,
  518. **     terminated by a pattern whose pattern string is NULL.
  519. **   flags - The flags specified in the FDB for the parse.
  520. **
  521. ** Output arguments: None
  522. ** Returns: TRUE if matching succeeds, FALSE otherwise.
  523. **
  524. ** Global variables:
  525. **   dttext - Pointer to the text to be matched, updated after a successful 
  526. **     match
  527. **   dtlen - Number of characters in input text, updated after success
  528. **   dtinc - TRUE after a failing match that failed because of insufficient
  529. **     input text.
  530. **   dtcmp - After an incomplete match, points to appropriate completion 
  531. **     text, or NULL if there is none.
  532. **/
  533.  
  534.  
  535. #ifdef TRACE
  536. static int tracelev = 0;                /* indentation level for trace */
  537. #endif
  538.  
  539. static int
  540. match(pats,flags)
  541. dtpat *pats;
  542. int flags;
  543. {
  544. #ifdef TRACE
  545.   int ret;                /* results of match operation */
  546.   tracelev +=2;
  547.   trindent();fprintf(stderr,"Matching: ");trshow();trnl();
  548.   tracelev +=2;
  549.   ret = match1(pats,flags);
  550.   tracelev -= 2;
  551.   trindent();fprintf(stderr,"Match result: %s\n",(ret ? "SUCCESS":"FAILURE"));
  552.   tracelev -= 2;
  553.   return(ret);
  554. }
  555. static int
  556. match1(pats,flags)
  557. dtpat *pats;
  558. int flags;
  559. {  
  560. #endif
  561.  
  562.   char *origtext = dttext;        /* save input parameters */
  563.   int origlen = dtlen;            /*  to be restored on failure */
  564.   char *savtext;            /* input params after a good match */
  565.   int savlen;
  566.   int rank;                /* ranks of individual patterns */
  567.   int hirank;                /* highest pattern rank */
  568.   dtpat *p;                /* for stepping through patterns */
  569.   int matched = FALSE;            /* TRUE if a pattern succeeds */
  570.   
  571.   hirank = -1;                /* assume no patterns eligible */
  572.   for (p = pats; p->_dppat != NULL; p++) { /* step through the patterns */
  573.     rank = patrank(p,flags);        /* compute ranks */
  574.     if (rank > hirank)
  575.       hirank = rank;            /* and remember highest rank */
  576.   }
  577.   
  578.   while (hirank >= 0) {            /* step down through eligible ranks */
  579.     for (p = pats; p->_dppat != NULL; p++) /* loop through pat at that rank */
  580.       if (patrank(p,flags) == hirank) {
  581.     if (matchpat(p,flags))        /* good match? */
  582. #ifdef TRACE
  583.         { tracelev -= 2; trindent(); fprintf(stderr,"PATTERN SUCCEEDED\n");
  584. #endif
  585.       if (!matched) {        /* first one? */
  586.         matched = TRUE;        /* then say we matched */
  587.         savtext = dttext;        /* save the input parameters */
  588.         savlen = dtlen;
  589.         set1val(p - pats);        /* set index of good pattern for */
  590.                     /*  return value for %r call */
  591.           }
  592.       else
  593.         return(FALSE);        /* multiple highest-rank matches */
  594. #ifdef TRACE
  595.         }
  596. #endif
  597.         else if (dtinc)            /* incomplete match? */
  598. #ifdef TRACE
  599.           { tracelev -= 2; trindent(); fprintf(stderr,"PATTERN FAILED (INC)\n");
  600. #endif
  601.       return(FALSE);        /* then quit now */
  602. #ifdef TRACE
  603.           }
  604.     else {
  605.       tracelev -= 2; trindent(); fprintf(stderr,"PATTERN FAILED\n");
  606.     }
  607. #endif
  608.  
  609.         dttext = origtext;        /* restore input for next pattern */
  610.     dtlen = origlen;
  611.       }
  612.     if (matched) {            /* single match at this rank? */
  613.       dttext = savtext;            /* fix global vars to consume input */
  614.       dtlen = savlen;
  615.       return(TRUE);            /* and succeed */
  616.     }
  617.     else
  618.       hirank--;                /* no match, try next lower rank */
  619.   }
  620.  
  621.   return(FALSE);            /* no success at any rank */
  622. }
  623.  
  624.  
  625.  
  626. /* patrank
  627. **
  628. ** Purpose:
  629. **   Compute the rank for a parse pattern with a given set of flags.
  630. **   If any of the flags disqualifies the pattern (via its _dpqal field),
  631. **   -1 is returned.  Otherwise, rank adjustments are applied, to the
  632. **   base rank, and the adjusted rank is returned.
  633. **
  634. ** Input args:
  635. **   pat - A pointer to the pattern to be ranked.
  636. **   flags - Parse flags active for the parse.
  637. **
  638. ** Output args: None
  639. ** Returns: Adjusted rank.
  640. **/
  641.  
  642. static int
  643. patrank(pat,flags)
  644. dtpat *pat;
  645. int flags;
  646. {
  647.   int rank;                /* base rank for the pattern */
  648.   rankadj *adj;                /* pointer to adjustment array */
  649.  
  650.   if (flags & pat->_dpqal)        /* any disqualifying flags active? */
  651.     return(-1);                /* yup, throw it out */
  652.  
  653.   rank = pat->_dprnk;            /* get the base rank */
  654.   adj = pat->_dpraj;            /* and point to adjustments array */
  655.   if (adj != NULL)
  656.     while (adj->_raflg != -1) {        /* loop through adjustments */
  657.       if (adj->_raflg & flags)        /* adjustment indicated by flags? */
  658.     rank += adj->_raadj;        /* yup, adjust the rank */
  659.       adj++;                /* and move to next adjustment */
  660.     }
  661.   return(rank);                /* give back adjusted rank */
  662. }
  663.  
  664.  
  665.  
  666. /* matchpat
  667. **
  668. ** Purpose:
  669. **   Apply a given pattern to the current input.  Return TRUE if all the
  670. **   required special pattern elements succeed and if intervening pattern 
  671. **   characters match the input.  Otherwise return FALSE.  After a success,
  672. **   the global input parameters (dttext and dtlen) will be left so as to
  673. **   consume the input characters that participated in the match.  On
  674. **   failure caused by insufficient input text, global variable dtinc
  675. **   will be set TRUE, and dtcmp may point to completion text to be
  676. **   filled in if completion is requested.
  677. **
  678. ** Input args:
  679. **   pat - A pointer to the pattern to be matched.
  680. **   flags - The flags in effect for the parse.
  681. **
  682. ** Output args: None
  683. ** Returns: TRUE for a successful match, FALSE for a failure.
  684. **/
  685.  
  686. static int
  687. matchpat(pat,flags)
  688. dtpat *pat;
  689. int flags;
  690. {
  691.   int **patarg = pat->_dparg;        /* point to pattern args */
  692.   char *pats = pat->_dppat;        /* and to pattern string */
  693.   char pc,tc;                /* chars from pat & dttext input */
  694.   char *opttext;            /* vars to save input state */
  695.   int optlen;                /*  before an optional pat element */
  696.   int opt;                /* TRUE iff during optional pat elt */
  697.   int nogood;                /* TRUE after a failing pattern elt */
  698.   char *delims;                /* pointer to a %d arg string */
  699.   int i;
  700.   
  701. #ifdef TRACE
  702.   trindent();fprintf(stderr,"Pattern [%s] Input [",pats);
  703.   trshow();fprintf(stderr,"]\n");
  704.   tracelev += 2;
  705. #endif
  706.  
  707.   clearval();                /* clear the pattern values */
  708.  
  709.   while ((pc = *pats++) != NULCHAR) {    /* scan through pattern string */
  710.     if (pc == SPACE)
  711.       continue;                /* skip spaces in pattern */
  712.     if (pc != '%') {            /* normal character */
  713. #ifdef TRACE
  714.       trindent();fprintf(stderr,"Char [%c] ",pc);
  715. #endif
  716.       if (dtlen == 0) {        /* reached end of input */
  717.     dtinc = TRUE;            /* signal incomplete match */
  718.     dtcmp = NULL;            /* no completion on normal chars */
  719. #ifdef TRACE
  720.         fprintf(stderr,"INC\n");
  721. #endif
  722.     return(FALSE);            /* and fail */
  723.       }
  724.       tc = *dttext++;            /* get next input char */
  725.       dtlen--;                /* and consume it */
  726.       if (islower(tc))            /* convert to upper case */
  727.     tc = toupper(tc);
  728.       if (islower(pc))
  729.     pc = toupper(pc);
  730.       if (pc != tc)
  731. #ifdef TRACE
  732.         { fprintf(stderr,"NO\n");
  733. #endif
  734.     return(FALSE);            /* fail if chars do not match */
  735. #ifdef TRACE
  736.         }
  737.       else
  738.         fprintf(stderr,"OK\n");
  739. #endif
  740.       continue;                /* otherwise continue scanning */
  741.     }
  742.     pc = *pats++;            /* get special element char */
  743. #ifdef TRACE
  744.     trindent();
  745. #endif
  746.     if (pc == '?') {            /* this element optional? */
  747. #ifdef TRACE
  748.       fprintf(stderr,"Optional ");
  749. #endif
  750.       opt = TRUE;            /* flag it */
  751.       opttext = dttext;            /* and save input state */
  752.       optlen = dtlen;
  753.       pc = *pats++;            /* and get next pattern char */
  754.     }
  755.     else
  756.       opt = FALSE;            /* else clear optional flag */
  757. #ifdef TRACE
  758.     fprintf(stderr,"Special [%c] Args [",pc); trargs();
  759.     fprintf(stderr,"] Input [");trshow();
  760.     fprintf(stderr,"] ");
  761. #endif
  762.     nogood = FALSE;            /* assume pat element will succeed */
  763.  
  764.     switch(pc) {            /* now dispatch to element handler */
  765.       case '=':                /* assign value from prior element */
  766.     **(patarg++) = patval[0];    /* assign value, move to next arg */
  767.     for (i = 1; i < MAXVAL; i++)    /* cycle the pattern value array */
  768.       patval[i-1] = patval[i];
  769.     patval[MAXVAL-1] = -1;        /* and clear the last value */
  770.     break;
  771.  
  772.       case '%':                /* match a percent sign */
  773.     if (dtlen-- == 0)        /* insufficient input? */
  774.       dtinc = nogood = TRUE;    /* yes, flag incomplete failure */
  775.         else if ((*dttext++) != '%')
  776.       nogood = TRUE;        /* check for next char match */
  777.     else
  778.       set1val(0);            /* returns 0 on success */
  779.     break;
  780.       
  781.       case SPACE:            /* match a space */
  782.     if (dtlen-- == 0)        /* insufficient input? */
  783.        dtinc = nogood = TRUE;    /* yes, flag incomplete failure */
  784.         else if ((*dttext++) != SPACE)
  785.       nogood = TRUE;        /* next char not a space */
  786.         else
  787.       set1val(0);            /* good match */
  788.     break;
  789.     
  790.       case 'c':                /* match next pattern char */
  791.       case 'C':
  792. #ifdef TRACE
  793.         fprintf(stderr,"Char [%c] ",*(pats+1));
  794. #endif
  795.     if (dtlen-- == 0)        /* insufficient input? */
  796.        dtinc = nogood = TRUE;    /* yes, flag incomplete failure */
  797.         else if ((*dttext++) != *pats++)
  798.       nogood = TRUE;        /* next char does not match */
  799.         else
  800.       set1val(0);            /* good match */
  801.     break;    
  802.  
  803.       case 'k':                /* keyword match */
  804.       case 'K':
  805.     nogood = !matchkey(*patarg++);  /* attempt the match */
  806.     break;
  807.  
  808.       case '#':                /* one of the numeric patterns */
  809.     nogood = !matchnum(*pats++);    /* attempt numeric parse */
  810.         break;
  811.  
  812.       case 'p':                /* next char punctuation */
  813.       case 'P':
  814.     if (dtlen <= 0) {        /* insufficient input? */
  815.       dtinc = nogood = TRUE;    /* then set incomplete failure */
  816.       dtcmp = " ";        /* complete with a space */
  817.         }
  818.     else {
  819.       tc = *dttext;            /* get next char, do not consume it */
  820.       if (isalnum(tc))        /* not a punctuation char? */
  821.         nogood = TRUE;        /* then signal failure */
  822.           else
  823.         set1val((int) tc);        /* return ASCII code of char */
  824.         }
  825.     break;
  826.     
  827.       case 'w':                /* match a run of whitespace */
  828.       case 'W':
  829.     if (dtlen <= 0) {        /* insufficient chars? */
  830.       dtinc = nogood = TRUE;    /* signal incomplete failure */
  831.       dtcmp = " ";            /* complete with a space */
  832.       break;
  833.         }
  834.     tc = *dttext;            /* get next input char, unquoted */
  835.     if ((tc != SPACE) && (tc != TAB)) /* first char not whitespace? */
  836.       nogood = TRUE;
  837.         else {
  838.       while ((--dtlen) > 0) {    /* loop through nonempty run */
  839.         tc = *(++dttext);
  840.         if ((tc != SPACE) && (tc != TAB))
  841.           break;            /* until non-whitespace encountered */
  842.           }
  843.       if (dtlen == 0)        /* if exited loop for lack of input */
  844.         dttext++;            /*  pointer was not bumped enough */
  845.           set1val(0);            /* always return zero */
  846.         }
  847.         break;
  848.  
  849.       case 'd':                /* match a delimiter char */
  850.       case 'D':
  851.     delims = (char *) *patarg++;    /* point to delimiter set */
  852. #ifdef TRACE
  853.         fprintf(stderr,"Delims [%s] ",delims);
  854. #endif
  855.     if (dtlen <= 0) {        /* no input left? */
  856.       nogood = dtinc = TRUE;    /* signal incomplete failure */
  857.       dtcmp = NULL;        /* no completion */
  858.         }
  859.     else {
  860.       nogood = TRUE;        /* assume first char not delimiter */
  861.       tc = *dttext++;            /* get next input char */
  862.       dtlen--;            /* and count it */
  863.       for (i = 0; delims[i] != NULCHAR; i++) /* loop through delimiters */
  864.         if (delims[i] == tc) {    /* found a match? */
  865.           nogood = FALSE;        /* signal success */    
  866.           set1val((int) i);        /* return index of matching delim */
  867.           break;
  868.             }
  869.         }
  870.     break;
  871.     
  872.       case 'r':                /* recursively invoke a pattern set */
  873.       case 'R':
  874. #ifdef TRACE
  875.         trnl();
  876. #endif
  877.     nogood = !match(*patarg++,flags); /* invoke the pattern matcher */
  878. #ifdef TRACE
  879.         trindent(); fprintf(stderr,"Result for %r: ");
  880. #endif
  881.     break;
  882.  
  883.       case 'f':                /* invoke a function */
  884.       case 'F':
  885.     nogood = !(*((int (*)()) *patarg++))(&i); /* get return value in i */
  886.     if (!nogood)
  887.       set1val(i);            /* set return value on success */
  888.     break;
  889.     
  890.       case 'z':                /* store a zero value */
  891.       case 'Z':
  892.     *(*patarg++) = 0;        /* set the value, move to next arg */
  893.     break;
  894.     
  895.       case 'n':                /* store a -1 value */
  896.       case 'N':
  897.     *(*patarg++) = -1;        /* set the value, move to next arg */
  898.     break;
  899.     
  900.       case '+':                /* skip next argument */
  901.     patarg++;            /* bump the pointer */
  902.     break;
  903.     
  904.       default:                /* anything else is an error */
  905.     nogood = TRUE;
  906.         break;
  907.     }
  908.     
  909.     if (dtinc)                /* incomplete parse? */
  910. #ifdef TRACE
  911.       { fprintf(stderr,"INC\n");
  912. #endif
  913.       return(FALSE);            /* yes, fail immediately */
  914. #ifdef TRACE
  915.       }
  916. #endif
  917.  
  918.     if (nogood)                /* pattern element failed? */
  919.       if (opt) {            /* optional element? */
  920. #ifdef TRACE
  921.         fprintf(stderr,"NO (OK)\n");
  922. #endif
  923.     clearval();            /* yes, all values to -1 */
  924.     dttext = opttext;        /* restore input pointers */
  925.     dtlen = optlen;
  926.       }
  927.       else
  928. #ifdef TRACE
  929.         { fprintf(stderr,"NO\n");
  930. #endif
  931.     return(FALSE);            /* fail if failing elt not optional */
  932. #ifdef TRACE
  933.         }
  934.     else
  935.        fprintf(stderr,"OK\n");
  936. #endif
  937.  
  938.   }
  939.   return(TRUE);                /* got through entire pattern */
  940. }
  941.  
  942.  
  943.  
  944. /* matchkey
  945. **
  946. ** Purpose:
  947. **   Implement the %k special pattern element.  Input characters are matched
  948. **   against the keywords in the given array until a matching key is located,
  949. **   or until they have all failed to match.  In the former case, the index
  950. **   of the matching keyword is set into the patval array as the value
  951. **   returned by this pattern element, and TRUE is returned.  In the latter
  952. **   case, FALSE is returned.  If matching fails on any keyword as a result
  953. **   of insufficient input, dtinc is set and an immediate failure is given.
  954. **   A keyword matches the input only if the input contains all the characters
  955. **   contained in the keyword up to a vertical bar in the keyword.  After 
  956. **   that, additional matching characters are consumed as well, up to the
  957. **   first nonmatching character or the end of the keyword.  Case is ignored
  958. **   when matching letters.
  959. **
  960. **   In the case that the end of the input is reached (causing dtinc to be
  961. **   set), if the current input uniquely matches exactly one of the keywords,
  962. **   the remainder of that keyword is set as the completion text, even if
  963. **   the input has not reached the vertical bar marker for that keyword.
  964. ** Input arguments:
  965. **   keys - Pointer to an array of keyword strings, terminated by NULL.
  966. **
  967. ** Output arguments: None.
  968. ** Returns: TRUE for success, FALSE for failure or incomplete.
  969. **/
  970.  
  971.  
  972. static int
  973. matchkey(keys)
  974. char **keys;
  975. {
  976.   int len;                /* length of individual key match */
  977.   char *key;                /* pointer to individual key */
  978.   int sawbar;                /* TRUE if bar seen in matching key */
  979.   int i,j,k;
  980. #define KEYCMPLEN 15            /* size of following buffer */
  981.   static char keycmp[KEYCMPLEN];    /* keyword completion text buffer */
  982.  
  983. #ifdef TRACE
  984.   fprintf(stderr,"Keys [%s...] ",keys[0]);
  985. #endif
  986.   if (dtlen == 0) {            /* no input to match? */
  987.     dtinc = TRUE;            /* set incomplete flag */
  988.     dtcmp = NULL;            /* and no completion */
  989.     return(FALSE);            /* and fail immediately */
  990.   }
  991.       
  992.   for (i = 0; (key = keys[i]) != NULL; i++) { /* loop through the keys */
  993.     len = matchk1(dttext,dtlen,key,&sawbar); /* try to match current key */
  994.     if (dtinc || sawbar)
  995.       break;                /* exit on success or end of input */
  996.   }
  997.   
  998.   if (dtinc) {                /* ran out of input? */
  999.     key += len;                /* point to remainder of keyword */
  1000.     if (sawbar) key++;            /* account for bar if seen */
  1001.     for (j = k = 0; k < KEYCMPLEN-1; j++) /* copy it to our buffer */
  1002.       if (key[j] == '|')
  1003.     continue;            /* skip the bar */
  1004.       else if (key[j] == NULCHAR)
  1005.     break;                /* exit at end of key */
  1006.       else
  1007.     keycmp[k++] = key[j];        /* copy other chars */
  1008.     keycmp[k] = NULCHAR;        /* tie off completion buffer */
  1009.     dtcmp = keycmp;            /* assume this is our completion */
  1010.     if (!sawbar)            /* if less than minimum match */
  1011.       while ((key = keys[++i]) != NULL)    /*  then look for a 2nd match */
  1012.         if (matchk1(dttext,dtlen,key,&sawbar) == len) { 
  1013.       dtcmp = NULL;            /* if found, then just beep */
  1014.       break;
  1015.         }
  1016.     return(FALSE);            /* fail the match in any case */
  1017.   }
  1018.   else if (sawbar) {            /* successful match? */
  1019.     dttext += len;            /* update input pointers */
  1020.     dtlen -= len;
  1021.     set1val(i);                /* set pattern return value */
  1022.     return(TRUE);            /* and succeed */
  1023.   }
  1024.   else                    /* all keys failed outright */
  1025.     return(FALSE);            /* so just fail */
  1026. }
  1027.  
  1028.  
  1029.  
  1030. /* matchk1 - Aux routine for matchkey
  1031. **
  1032. ** Purpose:
  1033. **   Determine how much of the input text matches a given keyword, and
  1034. **   return the number of matching chars.   Set a return flag indicating
  1035. **   whether or not a vertical bar was passed during the match (meaning
  1036. **   that the text should be accepted as matching the keyword).
  1037. **
  1038. ** Input arguments:
  1039. **   text - A pointer to the text to be matched.
  1040. **   textlen - The length of the text to be matched.  Guaranteed not
  1041. **     to be zero.
  1042. **   key - A pointer to the null-terminated keyword text.
  1043. **
  1044. ** Output arguments:
  1045. **   sawbar - Set TRUE if a vertical bar is passed while matching
  1046. **     the key.  Otherwise, FALSE is set.
  1047. **/
  1048.  
  1049. static int
  1050. matchk1(text,textlen,key,sawbar)
  1051. char *text,*key;
  1052. int textlen,*sawbar;
  1053. {
  1054.   char tc,kc;                /* individual chars to compare */
  1055.   int len = 0;                /* number of chars matched */
  1056.  
  1057.   *sawbar = FALSE;            /* bar not seen yet */
  1058.   while (textlen != 0) {        /* loop through text */
  1059.     tc = *text++;            /* get next char */
  1060.     textlen--;                /* and count it */
  1061.     if ((kc = *key++) == NULCHAR) 
  1062.       break;                /* exit at end of key */
  1063.     if (kc == '|') {
  1064.       *sawbar = TRUE;            /* there's the bar! */
  1065.       if ((kc = *key++) == NULCHAR)    /* move to next char */
  1066.     break;
  1067.     }
  1068.     if (islower(tc))            /* convert to upper case */
  1069.       tc = toupper(tc);
  1070.     if (islower(kc))
  1071.       kc = toupper(kc);
  1072.     if (tc != kc)            /* chars don't match? */
  1073.       return(len);            /* return length of match */
  1074.     else
  1075.       len++;                /* count matching chars */
  1076.   }
  1077.  
  1078.   if (kc == '|')            /* input just barely met minimum? */
  1079.     *sawbar = TRUE;            /* yup, set flag */
  1080.   else if (kc != NULCHAR)        /* reached end of input? */
  1081.     dtinc = TRUE;            /* set incomplete flag */
  1082.   return(len);                /* give number of matching chars */
  1083. }
  1084.  
  1085.  
  1086.  
  1087. /* matchnum
  1088. **
  1089. ** Purpose:
  1090. **   Parse a decimal number in the current input, and check the value
  1091. **   according to the number type indicated by the passed character
  1092. **   (one of the characters that can follow "%#" in a pattern).
  1093. **   Set return value(s) according to the type, as well.
  1094. **
  1095. ** Input arguments:
  1096. **   type - Character selecting the treatment required for the parsed number.
  1097. **
  1098. ** Output args: None.
  1099. ** Returns: TRUE if successful parse, FALSE otherwise.
  1100. **/
  1101.  
  1102. static int
  1103. matchnum(type)
  1104. char type;
  1105. {
  1106.   int val = 0;                /* value parsed from data */
  1107.   char tc;                /* individual chars from input */
  1108.   int digcnt = 0;            /* number of digits in number */
  1109.  
  1110. #ifdef TRACE
  1111.   fprintf(stderr,"Type [%c] ",type);
  1112. #endif
  1113.   if (dtlen <= 0) {            /* no input left? */
  1114.     dtinc = TRUE;            /* set incomplete flag */
  1115.     dtcmp = NULL;            /* no completion text */
  1116.     return(FALSE);            /* and fail */
  1117.   }
  1118.   tc = *dttext;                /* get first char */
  1119.   if (!isdigit(tc))
  1120.     return(FALSE);            /* no digits to parse */
  1121.   while (isdigit(tc)) {            /* loop through digits */
  1122.     val = (val*10) + tc - '0';        /* add digits into value */
  1123.     digcnt++;                /* and count them */
  1124.     tc = *(++dttext);            /* grab the next character */
  1125.     if ((--dtlen) <= 0)            /* and count the last one */
  1126.       break;                /* exit loop if no more chars */
  1127.   }
  1128.  
  1129.   switch(type) {            /* now validate the number */
  1130.     case 'm':                /* month number, range 1-12 */
  1131.       if ((digcnt > 2) || (val < 1) || (val > 12))
  1132.     return(FALSE);            /* out of range */
  1133.       val--;                /* drop to zero origin */
  1134.       break;
  1135.     case 'd':                /* day number, range 1-31 */
  1136.       if ((digcnt > 2) || (val < 1) || (val > 31))
  1137.     return(FALSE);            /* out of range */
  1138.       val--;                /* drop to zero origin */
  1139.       break;
  1140.     case 'y':                /* 2-digit year number */
  1141.       if ((digcnt != 2) || (val > 99))    /* value out of range? */
  1142.     return(FALSE);
  1143.       break;                /* leave good value as is */
  1144.     case 'Y':                /* 4-digit year number */
  1145.       if ((digcnt < 3) || (digcnt > 4) || (val > 9999))
  1146.     return(FALSE);            /* out of range */
  1147.       break;                /* leave good value as is */
  1148.     case 'h':                /* hour in range 1-12 */
  1149.       if ((digcnt > 2) || (val < 1) || (val > 12))
  1150.     return(FALSE);            /* out of range */
  1151.       break;                /* no modificaton to value */
  1152.     case 'H':                /* hour in range 0-24 */
  1153.       if ((digcnt > 2) || (val > 24))
  1154.     return(FALSE);            /* out of range */
  1155.       break;                /* no modification to value */
  1156.     case 'M':                /* 4-digit hours-and-minutes */
  1157.       if ((digcnt < 3) || (digcnt > 4) || (val > 2400))
  1158.     return(FALSE);            /* out of range */
  1159.       if ((val % 100) > 59)
  1160.     return(FALSE);            /* minutes out of range */
  1161.       set2val(val/100,val%100);        /* break up the input */
  1162.       return(TRUE);            /* and succeed */
  1163.     case 's':                /* seconds or minutes, range 0-59 */
  1164.       if ((digcnt != 2) || (val > 59))
  1165.     return(FALSE);            /* out of range */
  1166.       break;                /* good value is left as is */
  1167.     default:                /* unknown type */
  1168.       return(FALSE);            /* just fail */
  1169.   }
  1170.   
  1171.   set1val(val);                /* set return value */
  1172.   return(TRUE);                /* and succeed */
  1173. }
  1174.  
  1175.  
  1176.  
  1177. /* Routines for manipulating return value array:
  1178. **   clearval - Set all values to -1.
  1179. **   set1val - Set first value, clear the rest to -1.
  1180. **   set2val - Set first two values, clear the rest to -1.
  1181. **
  1182. ** Input arguments:
  1183. **   val1 - First value to set (set1val and set2val only)
  1184. **   val2 - Second value to set (set2val only)
  1185. **
  1186. ** Output arguments: None.
  1187. ** Returns: Nothing.
  1188. **/
  1189.  
  1190. static
  1191. clearval()
  1192. {
  1193.   int i;
  1194.   for (i = 0; i < MAXVAL; i++)        /* loop through value array */
  1195.     patval[i] = -1;            /* and clear the entries */
  1196. }
  1197.  
  1198. static
  1199. set1val(val1)
  1200. int val1;
  1201. {
  1202.   int i;
  1203.   for (i = 1; i < MAXVAL; i++)        /* clear all but first entry */
  1204.     patval[i] = -1;
  1205.   patval[0] = val1;            /* and set given value */
  1206. }
  1207.  
  1208. static
  1209. set2val(val1,val2)
  1210. int val1,val2;
  1211. {
  1212.   int i;
  1213.   for (i = 2; i < MAXVAL; i++)        /* clear all but first two entries */
  1214.     patval[i] = -1;
  1215.   patval[0] = val1;            /* and set given values */
  1216.   patval[1] = val2;
  1217. }
  1218.  
  1219.  
  1220.  
  1221.  
  1222. #ifdef TRACE
  1223.  
  1224. /* Routines to print out trace information during the pattern match */
  1225.  
  1226. static
  1227. trindent()                /* indent to current trace level */
  1228. {
  1229.   int i;
  1230.   for (i = 0; i < tracelev; i++)
  1231.     fprintf(stderr," ");
  1232. }
  1233.  
  1234. static
  1235. trshow()                /* show remaining unparsed input */
  1236. {
  1237.   int i;
  1238.   char c;
  1239.   for (i = 0; i < dtlen; i++) {
  1240.     c = dt[i];
  1241.     if ((c == '\177') || (c < SPACE))
  1242.       fprintf(stderr,"^%c",c^'\100');
  1243.     else
  1244.       fprintf(stderr,"%c",c);
  1245.   }
  1246. }
  1247.  
  1248. static
  1249. trargs()                /* print contents of patval array */
  1250. {
  1251.   int i;
  1252.   for (i = 0; i < MAXVAL; i++) {
  1253.     if (i != 0)
  1254.       fprintf(stderr," ");
  1255.     fprintf(stderr,"%d",patval[i]);
  1256.   }
  1257. }
  1258.  
  1259. static
  1260. trnl()                    /* print a newline */
  1261. {
  1262.   fprintf(stderr,"\n");
  1263. }
  1264.  
  1265. #endif
  1266.